winbrew_app\operations\repair/
restore.rs

1use std::fs;
2use std::path::{Path, PathBuf};
3
4use anyhow::{Context, Result};
5
6use crate::core::{fs::cleanup_path, network::installer_filename, temp_workspace};
7use crate::engines;
8use crate::operations::install;
9
10use super::ResolvedFileRestoreTarget;
11
12/// Restore the drifting files from a staged package tree.
13pub fn restore_file_restore_target(
14    target: &ResolvedFileRestoreTarget,
15    target_paths: &[PathBuf],
16) -> Result<usize> {
17    let temp_root =
18        temp_workspace::build_temp_root(&target.package.name, &target.package.version.to_string());
19    cleanup_path(&temp_root)?;
20    fs::create_dir_all(&temp_root)?;
21
22    let result = (|| -> Result<usize> {
23        let stage_dir = temp_root.join("stage");
24        let client = install::download::build_client()?;
25        let download_path = temp_root.join(installer_filename(&target.installer.url));
26
27        install::download::download_installer(
28            &client,
29            &target.installer,
30            &download_path,
31            false,
32            |_| {},
33            |_| {},
34        )?;
35
36        let resolved_kind =
37            engines::probe_installer_from_download(&target.installer, &download_path)?;
38        let mut resolved_installer = target.installer.clone();
39        resolved_installer.kind = resolved_kind;
40        let engine = engines::resolve_engine_for_installer(&resolved_installer)?;
41
42        let _ = install::flow::execute_engine_install(
43            engine,
44            &resolved_installer,
45            &download_path,
46            &stage_dir,
47            &target.package.name,
48        )?;
49
50        restore_target_files(
51            &stage_dir,
52            Path::new(&target.installed_package.install_dir),
53            target_paths,
54        )
55    })();
56
57    let _ = cleanup_path(&temp_root);
58
59    result
60}
61
62pub(crate) fn restore_target_files(
63    stage_dir: &Path,
64    install_dir: &Path,
65    target_paths: &[PathBuf],
66) -> Result<usize> {
67    let mut restored = 0usize;
68
69    for target_path in target_paths {
70        let relative_path = target_path.strip_prefix(install_dir).with_context(|| {
71            format!(
72                "failed to derive restored file path for {} from {}",
73                target_path.display(),
74                install_dir.display()
75            )
76        })?;
77        let source_path = stage_dir.join(relative_path);
78
79        if let Some(parent) = target_path.parent() {
80            fs::create_dir_all(parent).with_context(|| {
81                format!(
82                    "failed to prepare parent directory for {}",
83                    target_path.display()
84                )
85            })?;
86        }
87
88        fs::copy(&source_path, target_path).with_context(|| {
89            format!(
90                "failed to restore file {} from staged package",
91                target_path.display()
92            )
93        })?;
94
95        restored += 1;
96    }
97
98    Ok(restored)
99}
100
101#[cfg(test)]
102mod tests {
103    use super::restore_target_files;
104    use anyhow::Result;
105    use std::fs;
106    use tempfile::tempdir;
107
108    #[test]
109    fn restore_target_files_copies_staged_content() -> Result<()> {
110        let root = tempdir().expect("temp dir");
111        let stage_dir = root.path().join("stage");
112        let install_dir = root.path().join("packages").join("Contoso.App");
113        let target_path = install_dir.join("bin").join("tool.exe");
114        let staged_path = stage_dir.join("bin").join("tool.exe");
115
116        fs::create_dir_all(staged_path.parent().expect("stage parent")).expect("stage dir");
117        fs::create_dir_all(target_path.parent().expect("target parent")).expect("target dir");
118        fs::write(&staged_path, b"restored-binary").expect("write staged file");
119
120        let restored =
121            restore_target_files(&stage_dir, &install_dir, std::slice::from_ref(&target_path))?;
122
123        assert_eq!(restored, 1);
124        assert_eq!(
125            fs::read(&target_path).expect("read target"),
126            b"restored-binary"
127        );
128
129        Ok(())
130    }
131}